package proxyPool

import (
	"net/url"
	"sync/atomic"
	"time"
)

// Node 决定当前节点是否有效, 例如可被使用的次数\过期时间等都应该在节点自身校验.
type Node interface {
	Url() *url.URL
	IsValid() bool  // 是否有效
	InvalidateNow() // 立即失效该节点
	Use()           // 节点被使用时会调用该方法
}

type expirationNode struct {
	url      *url.URL
	expireAt time.Time
	invalid  *atomic.Bool
}

func (e *expirationNode) Url() *url.URL { return e.url }

func (e *expirationNode) Use() {}

func (e *expirationNode) IsValid() bool { return !e.invalid.Load() && time.Now().Before(e.expireAt) }

func (e *expirationNode) InvalidateNow() { e.invalid.Store(true) }

// NewNodeWithExpirationTime 代理将在某个时间点后被标记过期
func NewNodeWithExpirationTime(u *url.URL, t time.Time) Node {
	return &expirationNode{url: u, expireAt: t, invalid: &atomic.Bool{}}
}

// NewNodeWithExpirationDuration 代理将在 d 后被标记过期
func NewNodeWithExpirationDuration(u *url.URL, d time.Duration) Node {
	return NewNodeWithExpirationTime(u, time.Now().Add(d))
}

type maxUsesNode struct {
	url     *url.URL
	invalid *atomic.Bool
	maxUses uint64
	used    uint64
}

func (m *maxUsesNode) Url() *url.URL {
	return m.url
}

func (m *maxUsesNode) Use() { atomic.AddUint64(&m.used, 1) }

func (m *maxUsesNode) IsValid() bool {
	return !m.invalid.Load() && atomic.LoadUint64(&m.used) < m.maxUses
}

func (m *maxUsesNode) InvalidateNow() { m.invalid.Store(true) }

// NewNodeWithMaxUses 代理在调用 IsValid m次后会被标记过期
func NewNodeWithMaxUses(u *url.URL, m uint64) Node {
	return &maxUsesNode{url: u, maxUses: m, invalid: &atomic.Bool{}}
}

type expirationMaxUsesNode struct {
	url      *url.URL
	expireAt time.Time
	maxUses  uint64
	used     uint64
	invalid  *atomic.Bool
}

func (e *expirationMaxUsesNode) Url() *url.URL {
	return e.url
}

func (e *expirationMaxUsesNode) Use() { atomic.AddUint64(&e.used, 1) }

func (e *expirationMaxUsesNode) IsValid() bool {
	return !e.invalid.Load() && time.Now().Before(e.expireAt) && atomic.LoadUint64(&e.used) < e.maxUses
}

func (e *expirationMaxUsesNode) InvalidateNow() { e.invalid.Store(true) }

// NewNodeWithExpirationTimeMaxUses 代理将在 某个时间点 / 调用 IsValid m次 后被标记过期
func NewNodeWithExpirationTimeMaxUses(u *url.URL, t time.Time, m uint64) Node {
	return &expirationMaxUsesNode{url: u, expireAt: t, maxUses: m, invalid: &atomic.Bool{}}
}

// NewNodeWithExpirationDurationMaxUses 代理将在 d / 调用 IsValid m次 后被标记过期
func NewNodeWithExpirationDurationMaxUses(u *url.URL, d time.Duration, m uint64) Node {
	return NewNodeWithExpirationTimeMaxUses(u, time.Now().Add(d), m)
}
